import os
import sys
import time
import subprocess

from PyQt5.QtCore import QRunnable, QObject, pyqtSignal, QThread, QThreadPool, QTimer
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import QDialog, QMainWindow, QFileDialog, QApplication, QMessageBox, QLCDNumber, QSplashScreen, \
    QAction

from ui.main_ui import *
from ui.waiting import *

log = None  # 全局变量，存储采样算法的日志信息
data_file = None  # 数据集文件
parent_file = None  # 父结点文件
node_file = None  # 结点取值数量文件
result_path = None  # 采样结果文件存储路径
exit_flag = 0  # 采样算法是否运行成功的标志，0表示成功，其他表示不成功
count = 0  # 计时器
finish_flag = False  # 采样算法是否运行完毕的标志，False表示未运行完
cur_ws = os.getcwd()  # 记录当前工作路径
data_dir = cur_ws.replace("\\", "/")  # 记录数据集文件


class DialogWindow(QDialog, Ui_Dialog):
    """
    显示等待窗口的类
    """
    def __init__(self, parent=None):
        super(DialogWindow, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle("等待窗口")
        # 设置模态
        self.setWindowModality(True)
        # 设置LCDNumber
        self.lcd_timer = QLCDNumber(self)
        self.lcd_timer.setSegmentStyle(QLCDNumber.Flat)  # 显示样式
        self.lcd_timer.setSmallDecimalPoint(False)  # 整数
        self.lcd_timer.setDigitCount(7)  # 七位数
        self.lcd_timer.setMode(QLCDNumber.Dec)  # 十进制显示
        self.lcd_timer.display(0)
        self.lcd_timer.setGeometry(120, 140, 261, 81)
        self.lcd_timer.setObjectName("lcd_timer")

    def update_lcd_timer(self):
        global count
        count = count + 1
        self.lcd_timer.display(count)


# 这样，信号可以定义在类的构造函数__init__中
class NewSignal(QObject):
    signal = pyqtSignal()


class SampleThread(QRunnable):

    def __init__(self, name):
        super(SampleThread, self).__init__()
        self.name = name
        # 继承自QRunnable的线程类必须这样写
        # 不能像继承QThread的线程类那样写在任何函数外面
        self.start_signal = NewSignal()
        # 采样算法运行出错（三个配置文件出现），发出错误信号
        self.error_signal = NewSignal()

    def run(self):
        global log, data_file, parent_file, node_file, result_path, exit_flag, cur_ws
        print("SampleThread中run函数正在运行", flush=True)
        print("SampleThread", QThread().currentThreadId(), flush=True)
        os.chdir("../exe/")  # 改变工作目录
        # data_file = "E:/test_exe/data/asia/data_20000.txt"
        # parent_file = "E:/test_exe/data/asia/parent.txt"
        # node_file = "E:/test_exe/data/asia/nodeValueNum.txt"
        result_path = os.path.split(cur_ws)[0] + "\\data\\result"
        result_path = result_path.replace("\\", "/")
        if not os.path.exists(result_path):
            os.makedirs(result_path)
        path = r"test.exe %s %s %s %s" % (data_file, parent_file, node_file, result_path)
        exit_flag = subprocess.call(path)
        # 三个配置文件出错，发出错误信号
        if exit_flag != 0:
            self.error_signal.signal.emit()
            print("采样算法运行失败！！！")
            # 恢复为原来的工作目录
            os.chdir(cur_ws)
            return
        print("完成采样算法！！！！！！！！！！！！！！")
        log = open(result_path + "\\log.txt", "r")
        print("完成读取log文件！！！！！！！！！！！！！！！！！！！！！！！！！")
        # 恢复为原来的工作目录
        os.chdir(cur_ws)
        print("SampleThread中run函数运行结束", flush=True)
        self.start_signal.signal.emit()  # 发射结束信号


class TimerThread(QRunnable):
    def __init__(self, parent=None):
        super(TimerThread, self).__init__()
        self.parent = parent  # 计时器的父组件
        self.update_signal = NewSignal()

    def run(self):
        global finish_flag
        print("TimerThread中的run函数正在运行", flush=True)
        print("TimerThread", QThread().currentThreadId(), flush=True)
        while True:
            if finish_flag:
                break
            time.sleep(1)
            self.update_signal.signal.emit()
        print("TimerThread中run函数运行结束", flush=True)


class UiWithSignal(MainUi):
    # 采样算法完成，通知等待窗口结束
    stop_signal = pyqtSignal()
    # 三个相关配置文件未完成，通知操作者进行配置
    none_signal = pyqtSignal()
    # 采样算法错误结束
    error_end_signal = pyqtSignal()

    def __init__(self):
        super(UiWithSignal, self).__init__()
        # 创建线程池
        self.pool = QThreadPool()
        # 全局线程
        self.pool.globalInstance()
        # 线程池中最大的线程数量
        self.pool.setMaxThreadCount(10)
        # 初始化信号
        self.init_connect()

        # 创建菜单栏MenuBar
        self.menu_bar = self.menuBar()
        self.file = self.menu_bar.addMenu("文件")
        self.open_data_action = QAction(qtawesome.icon("fa.file-text"), "打开数据集文件", self)
        self.open_data_action.setStatusTip("打开数据集文件")
        self.open_data_action.setShortcut("Ctrl+Shift+I")
        self.open_data_action.triggered.connect(self.slot_get_data)
        self.file.addAction(self.open_data_action)
        # 打开结点取值文件
        self.open_node_action = QAction(qtawesome.icon("fa.file-text"), "打开结点取值文件", self)
        self.open_node_action.setStatusTip("打开结点取值文件")
        self.open_node_action.setShortcut("Ctrl+Shift+O")
        self.open_node_action.triggered.connect(self.slot_get_node)
        self.file.addAction(self.open_node_action)
        # 打开父结点文件
        self.open_parent_action = QAction(qtawesome.icon("fa.file-text"), "打开父结点文件", self)
        self.open_parent_action.setStatusTip("打开父结点文件")
        self.open_parent_action.setShortcut("Ctrl+Shift+P")
        self.open_parent_action.triggered.connect(self.slot_get_parent)
        self.file.addAction(self.open_parent_action)
        self.file.addSeparator()
        # 保存
        self.save_as_action = QAction(qtawesome.icon("fa.save"), "另存为", self)
        self.save_as_action.setStatusTip("保存结果")
        self.save_as_action.setShortcut("Ctrl+S")
        self.save_as_action.triggered.connect(self.save_result)
        self.file.addAction(self.save_as_action)
        self.file.addSeparator()
        # 退出
        self.exit_action = QAction(qtawesome.icon("fa.window-close"), "退出", self)
        self.exit_action.setStatusTip("退出程序")
        self.exit_action.setShortcut("Ctrl+Shift+Q")
        self.exit_action.triggered.connect(self.exit_prog)
        self.file.addAction(self.exit_action)

        self.run = self.menu_bar.addMenu("运行")
        # 退出
        self.sample_action = QAction(qtawesome.icon("fa.play"), "抽样", self)
        self.sample_action.setStatusTip("抽样")
        self.sample_action.setShortcut("Ctrl+Shift+S")
        self.sample_action.triggered.connect(self.sample)
        self.run.addAction(self.sample_action)

        self.about = self.menu_bar.addMenu("关于")

        self.menu_bar.setStyleSheet('''
            QMenuBar{
                
            }
        ''')


    def init_connect(self):
        """
        初始化一些信号
        """
        # 三个配置文件信号与槽
        self.btn_data.clicked.connect(self.slot_get_data)
        self.btn_parent.clicked.connect(self.slot_get_parent)
        self.btn_node.clicked.connect(self.slot_get_node)
        # 按钮btn_sample单击触发采样算法线程sample
        self.btn_sample.clicked.connect(self.sample)
        # 采样算法完成，显示通知窗口
        self.stop_signal.connect(self.notice_dialog)
        # 保存结果
        self.btn_save.clicked.connect(self.save_result)
        self.btn_change.clicked.connect(self.change)

    def slot_get_data(self):
        """
        读取数据集文件
        """
        global data_file, data_dir
        data_file, _ = QFileDialog.getOpenFileName \
            (self, "打开数据文件", data_dir, "文本文件(*.txt)")
        if data_file != "":
            data_dir = os.path.split(data_file)[0]
            self.tb_conf.append("打开数据文件：" + data_file)
        else:
            print("未正确打开数据集文件data_file，请重新打开")
            data_file = None
            self.tb_conf.append("未正确打开数据集文件，请重新打开!")

    def slot_get_parent(self):
        """
        读取父结点文件
        """
        global parent_file, data_dir
        parent_file, _ = QFileDialog.getOpenFileName \
            (self, "打开父结点文件", data_dir, "文本文件(*.txt)")
        if parent_file != "":
            data_dir = os.path.split(parent_file)[0]
            self.tb_conf.append("打开父结点文件：" + parent_file)
        else:
            print("未正确打开父结点文件parent_file，请重新打开")
            parent_file = None
            self.tb_conf.append("未正确打开父结点文件，请重新打开!")

    def slot_get_node(self):
        """
        读取结点数量文件
        """
        global node_file, data_dir
        node_file, _ = QFileDialog.getOpenFileName \
            (self, "打开结点取值数量文件", data_dir, "文本文件(*.txt)")
        if node_file != "":
            data_dir = os.path.split(node_file)[0]
            self.tb_conf.append("打开结点取值文件：" + node_file)
        else:
            print("未正确打开结点取值文件node_file，请重新打开")
            node_file = None
            self.tb_conf.append("未正确打开结点取值文件，请重新打开!")

    def save_result(self):
        global cur_ws
        result = self.tb_log.toPlainText()
        file_name, _ = QFileDialog.getSaveFileName(self, "文件保存", data_dir, "文本文件(*.txt);;全部文件(*.*)")
        if file_name != "":
            fp = open(file_name, "w")
            fp.write(result)

    def change(self):
        if self.bottom_lb1.text() == "采样算法结果（抽取出的样本在原样本的编号）":
            self.bottom_lb1.setText("采样算法结果（样本取值）")
            self.tb_log.clear()
            with open(result_path + "/sub_sample.txt", "r") as fp:
                self.tb_log.append(fp.read())
        elif self.bottom_lb1.text() == "采样算法结果（样本取值）":
            self.bottom_lb1.setText("采样算法结果（抽取出的样本在原样本的编号）")
            self.tb_log.clear()
            with open(result_path + "/sub_number.txt", "r") as fp:
                self.tb_log.append(fp.read())

    def print_log(self):
        """
        将采样算法的日志信息输入到界面的文本框上
        """
        global log, data_file, parent_file, node_file, finish_flag
        print("print_log正在运行", flush=True)
        # 输出三个配置文件的信息
        self.tb_conf.clear()
        self.tb_conf.append("采样算法配置文件信息：")
        self.tb_conf.append("（1）数据集文件：" + data_file)
        self.tb_conf.append("（2）父结点文件：" + parent_file)
        self.tb_conf.append("（3）结点取值数量文件：" + node_file)
        self.tb_conf.append("-" * 70)
        self.tb_conf.append("-" * 70)
        QApplication.processEvents()
        # 输出采样算法日志
        for line in log:
            self.tb_conf.append(line.strip("\r\n"))
            QApplication.processEvents()
        with open(result_path + "/sub_number.txt", "r") as fp:
            self.tb_log.append(fp.read())
        finish_flag = True  # 采样算法完成
        # 重置为None
        log = None
        data_file = None
        parent_file = None
        node_file = None
        print("print_log运行结束", flush=True)
        # 发射采样算法结束信号
        self.stop_signal.emit()

    def show_dialog(self):
        """
        等待窗口界面
        """
        print("show_dialog函数正在运行", flush=True)
        # 等待窗口
        dialog = DialogWindow(self)
        dialog.show()
        timer_thread = TimerThread(dialog)
        timer_thread.update_signal.signal.connect(dialog.update_lcd_timer)
        # 采样算法结束信号stop_signal和关闭窗口的槽close对应
        self.stop_signal.connect(dialog.close)
        self.error_end_signal.connect(dialog.close)
        # 线程timer_thread运行，即运行WorkThread的成员函数run
        self.pool.start(timer_thread)
        print("show_dialog函数运行结束", flush=True)

    def sample(self):
        """
        采样算法
        """
        global data_file, parent_file, node_file, finish_flag, count, cur_ws
        print("sample函数正在运行", flush=True)
        os.chdir(cur_ws)
        finish_flag = False
        count = 0
        # 若三个配置文件至少有一个没有配置，则警告
        if data_file is None or parent_file is None or node_file is None:
            error_msg_box = QMessageBox()
            error_msg_box.setWindowIcon(QIcon("../icon/sample256.ico"))
            error_msg_box.setWindowTitle("配置文件错误")
            error_msg_box.setIconPixmap(QPixmap("../icon/warning64.ico"))
            error_msg_box.setText("请查看三个配置文件是否正确！")
            error_msg_box.setStandardButtons(QMessageBox.Ok)
            btn_ok = error_msg_box.button(QMessageBox.Ok)
            btn_ok.setText("确定")
            error_msg_box.exec_()
            self.none_signal.emit()
            print("由于三个配置文件未完成，sample函数提前运行结束", flush=True)
            return
        # 显示等待窗口
        self.show_dialog()
        # 清除文本框信息
        self.tb_log.clear()
        # 采样算法线程
        sample_thread = SampleThread("sample")
        sample_thread.start_signal.signal.connect(self.print_log)
        sample_thread.error_signal.signal.connect(self.error_dialog)
        # 开启线程
        self.pool.start(sample_thread)
        print("sample函数正在结束", flush=True)

    def notice_dialog(self):
        """
        程序运行成功的通知窗口
        """
        global count, finish_flag
        self.btn_save.setDisabled(0)
        self.btn_change.setDisabled(0)
        notice_msg_box = QMessageBox()
        notice_msg_box.setWindowIcon(QIcon("../icon/sample256.ico"))
        notice_msg_box.setWindowTitle("成功通知")
        notice_msg_box.setIconPixmap(QPixmap("../icon/information64.ico"))
        notice_msg_box.setText("程序运行成功，大概耗时%d秒。请查看结果。" % count)
        notice_msg_box.setStandardButtons(QMessageBox.Ok)
        btn_ok = notice_msg_box.button(QMessageBox.Ok)
        btn_ok.setText("确定")
        notice_msg_box.exec_()

    def error_dialog(self):
        """
        配置文件时配置错误的通知窗口
        """
        global data_file, parent_file, node_file, finish_flag
        finish_flag = True
        data_file = None
        parent_file = None
        node_file = None
        self.error_end_signal.emit()  # 采样算法异常结束
        error_msg_box = QMessageBox()
        error_msg_box.setWindowIcon(QIcon("../icon/sample256.ico"))
        error_msg_box.setWindowTitle("错误")
        error_msg_box.setIconPixmap(QPixmap("../icon/error64.ico"))
        error_msg_box.setText("请查看三个配置文件是否正确！")
        error_msg_box.setStandardButtons(QMessageBox.Ok)
        btn_ok = error_msg_box.button(QMessageBox.Ok)
        btn_ok.setText("确定")
        error_msg_box.exec_()

    # 添加中文的确认退出提示框
    # 主窗口MainWindow的事件closeEvent
    def closeEvent(self, event):
        # 创建一个消息盒子（提示框）
        quit_msg_box = QMessageBox()
        # 设置左上角图标
        quit_msg_box.setWindowIcon(QIcon("../icon/sample256.ico"))
        # 设置提示框的标题
        quit_msg_box.setWindowTitle('确认提示')
        # 设置提示框的内容
        quit_msg_box.setText('你确认退出吗？')
        # 设置确认标志
        quit_msg_box.setIconPixmap(QPixmap("../icon/ack64.ico"))
        # 设置按钮标准，一个yes一个no
        quit_msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        # 获取两个按钮并且修改显示文本
        btn_yes = quit_msg_box.button(QMessageBox.Yes)
        btn_yes.setText('确定')
        btn_no = quit_msg_box.button(QMessageBox.No)
        btn_no.setText('取消')
        quit_msg_box.exec_()
        print("退出事件")
        # 判断返回值，如果点击的是Yes按钮，我们就关闭组件和应用，否则就忽略关闭事件
        if quit_msg_box.clickedButton() == btn_yes:
            event.accept()
            print("退出程序，运行结束")
            os._exit(0)  # 结束整个程序
        else:
            event.ignore()
            print("取消退出程序")

    def exit_prog(self):
        # 创建一个消息盒子（提示框）
        quit_msg_box = QMessageBox()
        # 设置左上角图标
        quit_msg_box.setWindowIcon(QIcon("../icon/sample256.ico"))
        # 设置提示框的标题
        quit_msg_box.setWindowTitle('确认提示')
        # 设置提示框的内容
        quit_msg_box.setText('你确认退出吗？')
        # 设置确认标志
        quit_msg_box.setIconPixmap(QPixmap("../icon/ack64.ico"))
        # 设置按钮标准，一个yes一个no
        quit_msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        # 获取两个按钮并且修改显示文本
        btn_yes = quit_msg_box.button(QMessageBox.Yes)
        btn_yes.setText('确定')
        btn_no = quit_msg_box.button(QMessageBox.No)
        btn_no.setText('取消')
        quit_msg_box.exec_()
        print("退出事件")
        # 判断返回值，如果点击的是Yes按钮，我们就关闭组件和应用，否则就忽略关闭事件
        if quit_msg_box.clickedButton() == btn_yes:
            print("退出程序，运行结束")
            os._exit(0)  # 结束整个程序
        else:
            print("取消退出程序")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    pixmap = QPixmap("../icon/sample.png")
    splash = QSplashScreen(pixmap)
    splash.show()
    app.processEvents()
    win = UiWithSignal()
    splash.finish(win)
    win.show()
    sys.exit(app.exec_())
